package com.hyts.keygen.facade;

import com.hyts.keygen.bean.KeyScope;
import com.hyts.keygen.config.KeyGenProperties;
import com.hyts.keygen.config.KeyProperties;
import com.hyts.keygen.handler.KeyGenerator;
import com.hyts.keygen.handler.KeyIdGenerator;
import com.hyts.keygen.handler.KeyWorker;
import com.hyts.keygen.handler.KeyTimeHandler;
import com.hyts.properties.manager.PropertyManager;
import lombok.*;

import java.text.MessageFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.UUID;

/**
 * project-name:coffice-toolbox
 * package-name:com.hyts.keygen.facade
 * author:Libo/Alex
 * create-date:2019-05-26 12:00
 * copyright:libo-hyts-github
 * email:libo2dev@aliyun.com
 * description:此类主要用于: IdGenerator id自动生产器
 */
@Builder
@AllArgsConstructor
public class IdGenerator implements KeyGenerator {

    @Getter
    @Setter
    private Integer dataCenterId;

    @Getter
    @Setter
    private Integer workerId;

    @Getter
    @Setter
    private KeyWorker keyWorker;

    @Getter
    @Setter
    private KeyIdGenerator keyGenerator;


    /**********custom constructor code****************/

    public IdGenerator(){
        PropertyManager.getInstance().settingDefaultConfigurationName(KeyProperties.DEFAULT_CONFIG_FILE_NAME);
        Object workerId = PropertyManager.getInstance().getValue(KeyProperties.DEFAULT_WORKER_KEY);
        Object dataCenterId = PropertyManager.getInstance().getValue(KeyProperties.DEFAULT_DATA_CENTER_KEY);
        if(workerId == null || dataCenterId == null){
            throw new IllegalArgumentException(MessageFormat.format("配置文件:{0} - 属性:{1} 或者 - 属性:{2} 无法被解析！",
                    KeyProperties.DEFAULT_CONFIG_FILE_NAME,KeyProperties.DEFAULT_WORKER_KEY,KeyProperties.DEFAULT_DATA_CENTER_KEY));
        }else{
            this.dataCenterId = Integer.parseInt(dataCenterId+"");
            this.workerId = Integer.parseInt(workerId+"");
        }
        keyWorker = new KeyWorker(this.workerId,this.dataCenterId);
        keyGenerator = new KeyIdGenerator();
    }


    /**
     * classpath配置服务配置文件-支持文件类型为properties对象的配置数据结构
     * @param classPathConfigFile
     */
    public IdGenerator(String classPathConfigFile){
        PropertyManager.getInstance().settingDefaultConfigurationName(classPathConfigFile);
        Object workerId = PropertyManager.getInstance().getValue(KeyProperties.DEFAULT_WORKER_KEY);
        Object dataCenterId = PropertyManager.getInstance().getValue(KeyProperties.DEFAULT_DATA_CENTER_KEY);
        if(workerId == null || dataCenterId == null){
            throw new IllegalArgumentException(MessageFormat.format("配置文件:{0} - 属性:{1} 或者 - 属性:{2} 无法被解析！",
                    classPathConfigFile,KeyProperties.DEFAULT_WORKER_KEY,KeyProperties.DEFAULT_DATA_CENTER_KEY));
        }else{
            this.dataCenterId = Integer.parseInt(dataCenterId+"");
            this.workerId = Integer.parseInt(workerId+"");
        }
        keyWorker = new KeyWorker(this.workerId,this.dataCenterId);
        keyGenerator = new KeyIdGenerator();
    }

    /**
     * classpath配置服务配置文件-支持文件类型为properties对象的配置数据结构
     * @param classPathConfigFile
     * @param sequence
     */
    public IdGenerator(String classPathConfigFile,int sequence){
        PropertyManager.getInstance().settingDefaultConfigurationName(classPathConfigFile);
        Object workerId = PropertyManager.getInstance().getValue(KeyProperties.DEFAULT_WORKER_KEY);
        Object dataCenterId = PropertyManager.getInstance().getValue(KeyProperties.DEFAULT_DATA_CENTER_KEY);
        if(workerId == null || dataCenterId == null){
            throw new IllegalArgumentException(MessageFormat.format("配置文件:{0} - 属性:{1} 或者 - 属性:{2} 无法被解析！",
                    classPathConfigFile,KeyProperties.DEFAULT_WORKER_KEY,KeyProperties.DEFAULT_DATA_CENTER_KEY));
        }
        this.dataCenterId = Integer.parseInt(dataCenterId+"");
        this.workerId = Integer.parseInt(workerId+"");
        keyWorker = new KeyWorker(this.workerId,this.dataCenterId);
        keyGenerator = new KeyIdGenerator(sequence);
    }

    /**
     * classpath配置服务配置文件-支持文件类型为properties对象的配置数据结构
     * @param dataCenterId
     * @param workerId
     */
    public IdGenerator(int dataCenterId,int workerId){
        this.dataCenterId = Integer.parseInt(dataCenterId+"");
        this.workerId = Integer.parseInt(workerId+"");
        keyWorker = new KeyWorker(this.workerId,this.dataCenterId);
        keyGenerator = new KeyIdGenerator();
    }

    /**
     * classpath配置服务配置文件-支持文件类型为properties对象的配置数据结构
     * @param dataCenterId
     * @param workerId
     * @param sequence
     */
    public IdGenerator(int dataCenterId,int workerId,int sequence){
        this.dataCenterId = Integer.parseInt(dataCenterId+"");
        this.workerId = Integer.parseInt(workerId+"");
        keyWorker = new KeyWorker(this.workerId,this.dataCenterId);
        keyGenerator = new KeyIdGenerator(sequence);
    }

    @Override
    public Long nextId() {
        return keyWorker.nextId();
    }

    @Override
    public Long nextId(long tag) {
        return null;
    }

    /**
     * 获取uuid
     *
     * @return uuid
     */
    public static String getUUID(boolean ignoreHyphen) {
        String uuid = UUID.randomUUID().toString();
        if (ignoreHyphen) {
            return uuid.replaceAll("-", "");
        } else {
            return uuid;
        }
    }

    /**
     * 获取某个月的可能生成的最小与最大id对，这两个都是开放的，不包括在内
     *
     * <p>
     * <ul>
     * <li>最小值计算方式： 上个月可能达到的最大值
     * <li>最大值计算方式：下个月可能出现的最小值
     * </ul>
     *
     * @param year
     *            年
     * @param month
     *            月份 与Calendar#MONTH一致，从0开始，到11结束
     *
     * @see java.util.Calendar#MONTH
     *
     * @return 此时间段可能生成的最小与最大id对
     */
    public static KeyScope getIdPairOfMonth(int year, int month) {
        Calendar cal = new GregorianCalendar(year, month, 1);

        return getIdPair(cal, Calendar.MONTH);
    }

    /**
     * 获取某个月的可能生成的最小与最大id对，这两个都是开放的，不包括在内
     *
     * <p>
     * <ul>
     * <li>最小值计算方式： 上个月可能达到的最大值
     * <li>最大值计算方式：下个月可能出现的最小值
     * </ul>
     *
     * @return 此时间段可能生成的最小与最大id对
     */
    public static KeyScope getIdPairOfMonth(Date d) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(d);

        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH);

        return getIdPairOfMonth(year, month);
    }

    /**
     * 获取某一天的可能生成的最小与最大id对，这两个都是开放的，不包括在内 最小值计算方式： 上一天可能达到的最大值 最大值计算方式：
     * 下一天可能出现的最小值
     *
     * @return 此时间段可能生成的最小与最大id对
     */
    public static KeyScope getIdPairOfDay(int year, int month, int day) {
        Calendar cal = new GregorianCalendar(year, month, day);

        return getIdPair(cal, Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取某一天的可能生成的最小与最大id对 这两个都是开放的，不包括在内 最小值计算方式： 上一天可能达到的最大值 最大值计算方式：
     * 下一天可能出现的最小值
     *
     * @return 此时间段可能生成的最小与最大id对
     */
    public static KeyScope getIdPairOfDay(Date day) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(day);

        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH);
        int dayOM = cal.get(Calendar.DAY_OF_MONTH);

        return getIdPairOfDay(year, month, dayOM);
    }

    private static KeyScope getIdPair(Calendar cal, int field) {
        // 先计算最大值
        cal.add(field, 1);
        long nextTime = KeyTimeHandler.generate(cal.getTimeInMillis());
        long maxId = nextTime << (KeyGenProperties.TOTAL_BIT_COUNT - KeyGenProperties.TIMESTAMP_BIT_COUNT);

        cal.add(field, -1);
        long lastTime = KeyTimeHandler.generate(cal.getTimeInMillis()) - 1;
        long minId = lastTime << (KeyGenProperties.TOTAL_BIT_COUNT - KeyGenProperties.TIMESTAMP_BIT_COUNT)
                | 0xfff;
        KeyScope result = new KeyScope(minId, maxId);
        return result;
    }

    /**
     * 获取一个某个日期开始，一段秒数内的可能的最小与最大的id对
     *
     * @param date
     *            开始时间
     * @param secondsInMillis
     *            毫秒数
     *
     * @return 此时间段可能生成的最小与最大id对
     */
    public static KeyScope getIdPairOfDateWithSecInMillis(Date date, long secondsInMillis) {
        long nextTime = KeyTimeHandler.generate(date.getTime() + secondsInMillis + 1);
        long maxId = nextTime << (KeyGenProperties.TOTAL_BIT_COUNT - KeyGenProperties.TIMESTAMP_BIT_COUNT);
        long lastTime = KeyTimeHandler.generate(date) - 1;
        long minId = lastTime << (KeyGenProperties.TOTAL_BIT_COUNT - KeyGenProperties.TIMESTAMP_BIT_COUNT) | 0xfff;
        KeyScope result = new KeyScope(minId, maxId);
        return result;
    }

    /**
     * 获取一个某个日期开始，一段秒数内的可能的最小与最大的id对
     *
     * @param date
     *            开始时间
     * @param seconds
     *
     * @param date
     *            开始时间
     *
     * @return 此时间段可能生成的最小与最大id对
     */
    public static KeyScope getIdPairOfDateWithSec(Date date, long seconds) {
        long secondsInMillis = seconds * 1000;
        return getIdPairOfDateWithSecInMillis(date, secondsInMillis);
    }

    /**
     * 获取uuid
     *
     * @return uuid
     */
    public static String getUUID() {
        return getUUID(true);
    }
}
